#include "ffmpeg.h"

FFmpeg::FFmpeg(QObject *parent) :
    QObject(parent),
    is_stop_(false)
{
    video_stream_index_ = -1;
    avformat_network_init(); // 初始化网络流格式，使用RTPS网络流时必须先执行
    avformat_context_ = avformat_alloc_context(); // 申请一个AVFormatContext结构的内存，并进行简单初始化
    av_frame_ = av_frame_alloc(); // 申请一个AVFrame帧内存
    av_packet_ = av_packet_alloc();
}

FFmpeg::~FFmpeg()
{
    // 释放申请的各种内存
    avformat_free_context(avformat_context_);
    av_frame_free(&av_frame_);
    sws_freeContext(sws_context_);
    av_packet_unref(av_packet_);
    avcodec_free_context(&avcodec_context_);
}

void FFmpeg::setUrl(QString url)
{
    this->url_ = url;
}

bool FFmpeg::init()
{
    AVDictionary *format_opt = nullptr;
    av_dict_set(&format_opt, "stimeout", std::to_string(2*1000000).c_str(), 0);
    // 打开视频流
    qint32 result = avformat_open_input(&avformat_context_,
                                        url_.toStdString().c_str(),
                                        NULL, &format_opt);
    if (result < 0) {
        av_dict_free(&format_opt);
        qDebug() << "Failed to open video stream!";
        return false;
    }

    if (format_opt) {
        av_dict_free(&format_opt);
    }

    // 获取视频流信息
    result = avformat_find_stream_info(avformat_context_, NULL);
    if (result < 0) {
        qDebug() << "Failed to get video stream information";
        return false;
    }

    // 获取视频流索引
    video_stream_index_ = -1;
    for (uint i = 0; i < avformat_context_->nb_streams; i++) {
        if (avformat_context_->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream_index_ = i;
            break;
        }
    }
    if (video_stream_index_ == -1) {
        qDebug() << "Failed to get video stream index!";
        return false;
    }

    // 获取视频流的分辨率大小
    avcodec_context_ = avcodec_alloc_context3(nullptr);
    if (avcodec_context_ == nullptr) {
        qDebug() << "Could not allocate AVCodecContext";
        return false;
    }
    avcodec_parameters_to_context(avcodec_context_, avformat_context_->streams[video_stream_index_]->codecpar);
    // 视频宽
    video_width_ = avcodec_context_->width;
    // 视频高
    video_height_ = avcodec_context_->height;

    av_frame_rgb_ = alloc_picture(AV_PIX_FMT_RGB24, video_width_, video_height_);

    // 获取视频流解码器
    const AVCodec *avcodec = avcodec_find_decoder(avcodec_context_->codec_id);
    // 像素格式转换YUV--->RGB
    sws_context_ = sws_getContext(video_width_, video_height_, AV_PIX_FMT_YUV420P,   // 转换前的长、宽、像素格式
                                  video_width_, video_height_, AV_PIX_FMT_RGB24,     // 转换后的长、宽、像素格式
                                  SWS_BICUBIC,                                      // 转换方法  libswscale/swscale.h
                                  NULL, NULL, NULL);                                // 其他参数默认为空
    // 打开对应解码器
    result = avcodec_open2(avcodec_context_, avcodec, NULL);
    if (result < 0) {
        qDebug() << "Failed to open decoder!" ;
        return false;
    }
    qDebug() << "Success to init video stream!";
    return true;
}

void FFmpeg::play()
{
    qint32 ret;
    while (!is_stop_) { // av_read_frame()读取一帧数据
        if (av_read_frame(avformat_context_, av_packet_) >= 0) {
            if (av_packet_->stream_index == video_stream_index_) {
//                qDebug() << "Start decode:" << QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss");
                ret = avcodec_send_packet(avcodec_context_, av_packet_); // 将数据包放入解码器中，进行解码
                if (ret < 0) {
                    qDebug() << "Failed to send a package for decoding";
                    break;;
                }
                while (ret >= 0) {
                    ret = avcodec_receive_frame(avcodec_context_, av_frame_); //从缓存或者解码器内存中取出解压出来的帧数据，解码后的数据保存到AVFrame中，因为解码后的数据可能不止一包，所以使用while判断
                    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                        break;
                    }
//                    qDebug() << "Decoding frame:" << avcodec_context_->frame_number;
                    mutex_.lock();
                    sws_scale(sws_context_,
                              (const uint8_t* const *)av_frame_->data,  // 解码后的数据
                              av_frame_->linesize,                      // 每个通道的行字节数，linesize和width不同
                              0,                                        // 起始位置
                              video_height_,                            // 处理行数
                              av_frame_rgb_->data,                      // 目的buffer
                              av_frame_rgb_->linesize);
                    QImage image(av_frame_rgb_->data[0],
                            video_width_, video_height_,
                            QImage::Format_RGB888);
                    emit drawImage(image);
                    mutex_.unlock();
                }
            }
            av_packet_unref(av_packet_); //释放资源，否则内存会不停的上升
        }
    }
}

void FFmpeg::stopReadFrame()
{
    is_stop_ = true;
}

/**
 * @brief 申请一个宽为width高为height的AVFrame的内存
 * @param pix_fmt
 * @param width
 * @param height
 * @return
 */
AVFrame *FFmpeg::alloc_picture(AVPixelFormat pix_fmt, qint32 width, qint32 height)
{
    AVFrame *picture;
    qint32 ret;
    picture = av_frame_alloc();
    if (!picture) {
        return nullptr;
    }

    picture->format = pix_fmt;
    picture->width = width;
    picture->height = height;

    /* allocate the buffers for the frame data */
    ret = av_frame_get_buffer(picture, 0);
    if (ret < 0) {
        qDebug() << stderr << "Could not allocate frame data.";
        exit(1);
    }

    return picture;
}
